﻿using System;
using System.Collections;
using System.Collections.Generic;

using System.IO;
using System.Threading;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using MUtil;


public enum AutoLoadTextureEnum
{
    Single,
    MultiPle
}

/// <summary>
/// 自动加载主脚本
/// </summary>
public class AutoLoadManager : MonoBehaviour
{
    public string FilePath = null;

    public int ThreadLoadCount = 15; //每一个线程加载的图片数量

    private static string folderPath;

    private void Awake()
    {
        if (FilePath == null)
            folderPath = $"{Application.streamingAssetsPath }/AutoLoadImg";
        else
            folderPath = $"{Application.streamingAssetsPath }/AutoLoadImg/{FilePath}";

        EventManager.AddListener<TextureInfo>(AutoLoadTextureEnum.Single.ToString(), OnSingleTextureLoadResponse);
        EventManager.AddListener<List<TextureInfo>>(AutoLoadTextureEnum.MultiPle.ToString(), OnStartBatchProcess);
    }

    /// <summary>
    /// 单个图片处理监听
    /// </summary>
    /// <param name="info"></param>
    void OnSingleTextureLoadResponse(TextureInfo info)
    {
        StartCoroutine(LoadTexture(info));
    }

    /// <summary>
    /// 批量处理监听
    /// </summary>
    /// <param name="list"></param>
    void OnStartBatchProcess(List<TextureInfo> list)
    {
        new BatchProcess(ThreadLoadCount).OnMultiPleTextureLoadResponse(list);
    }

    /// <summary>
    /// 批量处理类，需要同时开启多个批量处理，new这个对象
    /// </summary>
    public class BatchProcess 
    {
        private List<TextureInfo> loadList = new List<TextureInfo>();

        private int ThreadNum = 0;

        private int threadLoadCount = 8;

        private List<MultiThreadDownload> threadList = new List<MultiThreadDownload>();

        public  bool[] threadStateList;

        public BatchProcess(int loadCount) { threadLoadCount = loadCount; }

        /// <summary>
        /// 批量处理
        /// </summary>
        /// <param name="infoList"></param>
        public void OnMultiPleTextureLoadResponse(List<TextureInfo> infoList)
        {
            loadList = infoList;
            ThreadNum = (int)Math.Ceiling(infoList.Count / (float)threadLoadCount);
            threadStateList = new bool[ThreadNum];

            MultiThreadDownload mtd;
            Thread thread;
            List<TextureInfo> threadInfoList;
            List<TextureData> threadDataList;

            for (int i = 0; i < ThreadNum; i++)
            {
                threadInfoList = new List<TextureInfo>();
                threadDataList = new List<TextureData>();

                for (int j = 0; j < threadLoadCount; j++)
                {
                    if ((i * threadLoadCount + j) < infoList.Count)
                    {
                        threadInfoList.Add(infoList[i * threadLoadCount + j]);
                    }
                }

                threadInfoList.ForEach(item =>
                {
                    threadDataList.Add(new TextureData(item.FileName, item.Type, item.Extensions, null));
                });

                mtd = new MultiThreadDownload();
                thread = new Thread(new ThreadStart(mtd.ExecuteThread));

                mtd.ThreadId =   i;
                mtd.thisThread = thread;
                mtd.batchProcess = this; 
                mtd.FolderPath = folderPath;
                mtd.infoList = threadInfoList;
                mtd.dataList = threadDataList;

                thread.Name = mtd.ThreadId.ToString();
                thread.IsBackground = true;

                threadList.Add(mtd);
                threadStateList[mtd.ThreadId] = false;

                thread.Start();

                Debug.Log("开启线程!");
            }

            MonoMgr.Instance.StartCoroutine(MonitorThreadStop());
        }

        /// <summary>
        /// 监听线程结束，所有线程都结束了，开始赋值
        /// </summary>
        /// <returns></returns>
        IEnumerator MonitorThreadStop()
        {
            bool IsMerge;

            while (true)
            {
                IsMerge = true;
                for (int i = 0; i < ThreadNum; i++)
                {
                    if (threadStateList[i] == false)
                    {
                        IsMerge = false;
                        yield return 0;

                        break;
                    }
                }
                if (IsMerge)
                    break;
            }

            for (int i = 0; i < ThreadNum; i++)
            {
                threadList[i].ThreadAbort();
            }
        }
    }

    /// <summary>
    /// 多线程加载
    /// </summary>
    public class MultiThreadDownload
    {
        public int ThreadId;
        public string FolderPath;
        public Thread thisThread;
        public BatchProcess batchProcess;
        public List<TextureData> dataList;
        public List<TextureInfo> infoList;

        /// <summary>
        /// 执行线程
        /// </summary>
        public void ExecuteThread()
        {
            System.Diagnostics.Debug.Print("开始读取!" + Thread.CurrentThread.Name);

            TextureData itemData;
            int loadCount = 0;

            while (dataList.Count > loadCount)
            {
                itemData = dataList[loadCount];

                var url = $"{FolderPath}/{itemData.FileName}.{ itemData.Extensions }";

                using (FileStream fs = new FileStream(url, FileMode.Open, FileAccess.Read))
                {

                    byte[] readBytes = new byte[fs.Length];
                    fs.Read(readBytes, 0, (int)fs.Length);

                    Thread.Sleep(1);

                    itemData.Data = readBytes;
                    dataList[loadCount] = itemData;

                    fs.Close();
                    fs.Dispose();
                }
                loadCount++;
            }

            dataList.ForEach(item => Debug.Log(item.FileName));

            Debug.Log("读取完成!" + Thread.CurrentThread.Name);

            batchProcess.threadStateList[ThreadId ] = true;
        }

        /// <summary>
        /// 关闭线程
        /// </summary>
        public void ThreadAbort()
        {
            thisThread.Abort();

            for (int i = 0; i < dataList.Count; i++)
            {
                for (int j = 0; j < infoList.Count; j++)
                {
                    if (dataList[i].FileName == infoList[j].FileName)
                    {
                        SetTexture(infoList[j], dataList[i]);
                        break;
                    }
                }
            }

            Debug.Log("关闭线程!"+ ThreadId);
        }

        /// <summary>
        /// 图片赋值
        /// </summary>
        /// <param name="info"></param>
        /// <param name="data"></param>
        private void SetTexture(TextureInfo info, TextureData data)
        {
            Texture2D tex;

            Debug.Log("读取加载!"+ info.FileName);

            switch (info.Extensions)
            {
                case FileExtensions.jpg:
                    tex = new Texture2D(2, 2, TextureFormat.RGB24, true);
                    break;
                case FileExtensions.png:
                    tex = new Texture2D(2, 2, TextureFormat.RGBA4444, true);
                    break;
                default:
                    tex = new Texture2D(2, 2, TextureFormat.RGBA32, true);
                    break;
            }
           
            if (data.Data == null || data.Data.Length <= 0)
            {
                info.Callback.Invoke(null, false);
            }
            else
            {
                tex.LoadImage(data.Data);
                info.Callback.Invoke(tex, true);
            }
        }
    }


    /// <summary>
    /// 加载一张图片
    /// </summary>
    /// <param name="info"></param>
    /// <returns></returns>
    private IEnumerator LoadTexture(TextureInfo info)
    {
        //拼接url
        var url = $"file://{folderPath}/{info.FileName}.{Enum.GetName(info.Extensions.GetType(), info.Extensions)}";
        //从指定url加载文件。
        var unityWebRequest = UnityWebRequest.Get(url);
        //等待图片加载完
        yield return unityWebRequest.SendWebRequest();

        if (string.IsNullOrEmpty(unityWebRequest.error))
        {
            Texture2D tex;

            switch (info.Extensions)
            {
                case FileExtensions.jpg:
                    tex = new Texture2D(2, 2, TextureFormat.RGB24, true);
                    break;
                case FileExtensions.png:
                    tex = new Texture2D(2, 2, TextureFormat.RGBA4444, true);
                    break;
                default:
                    tex = new Texture2D(2, 2, TextureFormat.RGBA32, true);
                    break;
            }
            //如果数据为空，或长度小于等于0，就回调失败方法。
            if (unityWebRequest.downloadHandler.data == null || unityWebRequest.downloadHandler.data.Length <= 0)
            {
                info.Callback.Invoke(null, false);
            }
            else
            {
                tex.LoadImage(unityWebRequest.downloadHandler.data);
                info.Callback.Invoke(tex, true);
            }
        }
        else
        {
            info.Callback.Invoke(null, false);
        }

        unityWebRequest.Dispose();
    }

    /// <summary>
    ///     安全校验
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns></returns>
    private bool Verification(string fileName)
    {
        //判断文件名
        if (string.IsNullOrEmpty(fileName))
        {
            Debug.LogError("要加载的文件名称不能为空");
            return false;
        }
        return true;
    }


    /// <summary>
    ///     加载的文件类型
    /// </summary>
    public enum FileTypes
    {
        Texture,
        Sprite
    }

    /// <summary>
    ///     文件后缀名
    /// </summary>
    public enum FileExtensions
    {
        png,
        jpg
    }

    /// <summary>
    ///     加载的贴图信息
    /// </summary>
    public struct TextureInfo
    {
        /// <summary>
        ///     文件名
        /// </summary>
        public string FileName;
        /// <summary>
        ///     文件类型
        /// </summary>
        public FileTypes Type;
        /// <summary>
        ///     文件后缀名
        /// </summary>
        public FileExtensions Extensions;

        /// <summary>
        ///     加载回调
        /// </summary>
        public Action<Texture2D, bool> Callback;

        public TextureInfo(string fileName, FileTypes type, FileExtensions extensions, Action<Texture2D, bool> callback)
        {
            FileName = fileName;
            Type = type;
            Extensions = extensions;
            Callback = callback;
        }
    }

    public struct TextureData
    {
        public string FileName;

        public FileTypes FileTypes;

        public FileExtensions Extensions; //png,jpg

        public byte[] Data;

        public TextureData(string fileName, FileTypes fileTypes, FileExtensions extensions, byte[] byteArray)
        {
            FileName = fileName;
            FileTypes = fileTypes;
            Extensions = extensions;
            Data = byteArray;
        }
    }
}